package com.prime.tutorial.moviestore.service.logicalview;

import com.prime.tutorial.moviestore.Constants;
import com.prime.tutorial.moviestore.dao.*;
import com.prime.tutorial.moviestore.domain.*;
import com.prime.tutorial.moviestore.service.logicalview.LogicalTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * Implementation of {@link LogicalTest} interface <br/>
 * @see LogicalTest
 * @author iryna.masyuk
 * @author yevtern
 */
@Service
public class LogicalTestImpl implements LogicalTest {

    @Autowired
    public LogicalTestImpl(LogicalViewDAO logicalViewDAO,
                           CountryDAO countryDAO,
                           StateDAO stateDAO,
                           CityDAO cityDAO,
                           StreetDAO streetDAO,
                           BuildingDAO buildingDAO,
                           ApartmentDAO apartmentDAO,
                           ModelDAO modelDAO,
                           DeviceDAO deviceDAO,
                           ModelTypeDAO modelTypeDAO,
                           PortTypeDAO portTypeDAO,
                           DevicePortDAO devicePortDAO,
                           PortDAO portDAO,
                           DeviceToDeviceDAO deviceToDeviceDAO) {
        this.logicalViewDAO = logicalViewDAO;
        this.countryDAO = countryDAO;
        this.stateDAO = stateDAO;
        this.cityDAO = cityDAO;
        this.streetDAO=streetDAO;
        this.buildingDAO=buildingDAO;
        this.apartmentDAO = apartmentDAO;
        this.modelDAO = modelDAO;
        this.deviceDAO=deviceDAO;
        this.modelTypeDAO = modelTypeDAO;
        this.portTypeDAO = portTypeDAO;
        this.devicePortDAO = devicePortDAO;
        this.portDAO = portDAO;
        this.deviceToDeviceDAO = deviceToDeviceDAO;
    }

    private LogicalViewDAO logicalViewDAO;
    private CountryDAO countryDAO;
    private StateDAO stateDAO;
    private CityDAO cityDAO;
    private StreetDAO streetDAO;
    private BuildingDAO buildingDAO;
    private ApartmentDAO apartmentDAO;
    private ModelDAO modelDAO;
    private ModelTypeDAO modelTypeDAO;
    private DeviceDAO deviceDAO;
    private PortTypeDAO portTypeDAO;
    private DevicePortDAO devicePortDAO;
    private PortDAO portDAO;
    private DeviceToDeviceDAO deviceToDeviceDAO;

    //main interface methods
    public List getCountriesWithDevices(){
        return logicalViewDAO.getCountriesWithDevices();
    };

    public List getCitiesWithDevices(int countryId){
        Long id = new Long(countryId);
        return logicalViewDAO.getCitiesWithDevices(id);
    };
    public List<Device> getCoreDevices(int cityId){
        Long id = new Long(cityId);
        return logicalViewDAO.getCoreDevices(id);
    };

    public List<Device> getChildDevices(int deviceId){
        List<Device> res = new ArrayList<Device>();
        Long id = new Long(deviceId);

        res.add(deviceDAO.loadById(id));
        res.addAll(logicalViewDAO.getChildDevices(id));
        return res;
    };
    public List<Model> getModelsByType(String typeName){
        ModelType mt = modelTypeDAO.findByType(typeName).get(0);
        return modelDAO.findByModelType(mt);
    };
     public List<DeviceToDevice> getChildConnections(int deviceId){
         Long l = new Long(deviceId);
         return logicalViewDAO.getChildConnections(l);
     };

    //creators
    public Device addDevice(String deviceName,
                            String typeName,
                            String streetName,
                            String buildingName,
                            String aprtName,
                            String description,
                            String countryName,
                            String cityName,
                            double x,
                            double y){
        //check country
        Country country;
        if (countryDAO.findByName(countryName).isEmpty()){
           country=new Country(countryName);
        }
        else{
           country=countryDAO.findByName(countryName).get(0);
        }

        //check city
        //TODO city in country?
        City city;
        if (cityDAO.findByName(cityName).isEmpty()){
           city=new City(cityName);
        }
        else{
           city=cityDAO.findByName(cityName).get(0);
        }

        //check street name
        //TODO street in city??
        Street street;
        if (streetDAO.findByName(streetName).isEmpty()){
           street=new Street(streetName);
        }
        else{
           street=streetDAO.findByName(streetName).get(0);
        }

        //check buiding
        //TODO building in street???
        Building building;
        if (buildingDAO.findByName(buildingName).isEmpty()){
           building = new Building(buildingName,x,y);
        }
        else{
           building=buildingDAO.findByName(buildingName).get(0);
        }

        //check apartment
        Apartment apartment;
        if (apartmentDAO.findByName(aprtName).isEmpty()){
           apartment = new Apartment(aprtName);
        }
        else{
           apartment = apartmentDAO.findByName(aprtName).get(0);
        }

        //create location
        Location l = new Location(country,
                                  city,
                                  street,
                                  building,
                                  apartment);

        //get model
        //Long lng = new Long(typeId);
        Model model = modelDAO.findByType(typeName).get(0);

        //get status PLANNED_INSTALLATION
        State planned = stateDAO.findByType(Constants.STATE_PLANNED_INSTALLATION).get(0);

        //create device
        Device d = new Device(l,planned,model,deviceName,description,x,y);

        //persist
        deviceDAO.persist(d);

        createPorts(d);
        //TODO if fail
        return d;
    };


    public boolean addConnections(int portIdFrom, int portIdTo){
        Long frId=new Long(portIdFrom);
        Port from = portDAO.loadById(frId);

        Long toId=new Long(portIdTo);
        Port to = portDAO.loadById(toId);
        //error
        if (from.equals(null) || to.equals(null)){
            return false;
        }
        //TODO check for compatible ports   ???

        //ok
        DevicePort dpFrom;
        if (!devicePortDAO.findByPort(from).isEmpty()){
            dpFrom = devicePortDAO.findByPort(from).get(0);
        }
        else{
            return false;
        }

        DevicePort dpTo = devicePortDAO.findByPort(to).get(0);

        if (!devicePortDAO.findByPort(from).isEmpty()){
            dpTo = devicePortDAO.findByPort(to).get(0);
        }
        else{
            return false;
        }


        DeviceToDevice d2d=new DeviceToDevice(dpTo,dpFrom);
        deviceToDeviceDAO.persist(d2d);

        from.setConnected(true);
        from.setEnabled(true);
        to.setConnected(true);
        to.setEnabled(true);
        portDAO.update(from);
        portDAO.update(to);
        
        return true;
    };
    public boolean changeDevicePosition(int deviceId, double x, double y){
        //find device
        Long id = new Long(deviceId);
        Device d = deviceDAO.loadById(id);

        if (d.equals(null)) return false;

        //everything's ok - setting new coordinates
        d.setX(x);
        d.setY(y);

        //updating device
        deviceDAO.update(d);

        return true;
    };

    public boolean changeDeviceName(int deviceId, String name){
        if (name.equals("")) return false;
        Long id = new Long(deviceId);
        Device d = deviceDAO.loadById(id);

        if (d.equals(null)) return false;

        d.setName(name);
        deviceDAO.update(d);
        return true;
    };
    public List<Port> getPorts(int deviceId){
        Long id = new Long(deviceId);
        return logicalViewDAO.getPorts(id);
    }
    //getters
    public Country getCountryById(Long id){
        return countryDAO.loadById(id);
    };
    public List<DevicePort> getDevicePortByDevice(Device d){
        return devicePortDAO.findByDevice(d);
    };
    public Country getCountryByName(String name){
      return countryDAO.findByName(name).get(0);
    };
    public Model getModelByName(String modelName){
        return modelDAO.findByType(modelName).get(0);
    };
    public boolean deleteConnection(int fromPortId, int toPortId){
        //TODO test
        Long frId=new Long(fromPortId);
        Port from = portDAO.loadById(frId);

        Long toId=new Long(toPortId);
        Port to = portDAO.loadById(toId);
        //error
        if (from.equals(null) || to.equals(null)){
            return false;
        }
        //ok
        DevicePort dpFrom;
        if (!devicePortDAO.findByPort(from).isEmpty()){
            dpFrom = devicePortDAO.findByPort(from).get(0);
        }
        else{
            return false;
        }

        DevicePort dpTo = devicePortDAO.findByPort(to).get(0);

        if (!devicePortDAO.findByPort(from).isEmpty()){
            dpTo = devicePortDAO.findByPort(to).get(0);
        }
        else{
            return false;
        }

        //TODO not working


        return false;
    };

    public boolean deleteDevice(int deviceId){
        //TODO implement
        return false;
    }
    public List<Model> getModelsByType(int deviceId, String typeName){
        return logicalViewDAO.getModelsByType(typeName, deviceId);
    }
    public List<ModelType> getDeviceTypesByDeviceId(int deviceId){
        return modelTypeDAO.findByType(Constants.MODELTYPE_SWITCH);
    }
    //--------------------private functions

    /**
     * Creates ports on device
     * @param d device
     */
    private void createPorts(Device d){
        //TODO bad hardcoding!!! refactor
        PortType eth = portTypeDAO.findByType(Constants.PORT_TYPE_ETHERNET).get(0);
        PortType ser = portTypeDAO.findByType(Constants.PORT_TYPE_SERIAL).get(0);
        PortType lre = portTypeDAO.findByType(Constants.PORT_TYPE_LRE).get(0);
        PortType rj11 = portTypeDAO.findByType(Constants.PORT_TYPE_RJ11).get(0);
        //routers
        if (d.getModel().getType().equals(Constants.MODEL_ROUTER_CISCO3945)){
           for(int i=0; i<3; i++){
               createPortOnDevice(d,eth.getType()+" 0/"+i,eth);
           }
           for(int i=0; i<2;i++){
               createPortOnDevice(d,ser.getType()+" 0/"+i,ser);
           }
         }
         if (d.getModel().getType().equals(Constants.MODEL_ROUTER_CISCO2951)){
           for(int i=0; i<3; i++){
               createPortOnDevice(d,eth.getType()+" 0/"+i,eth);
           }
           for(int i=0; i<2;i++){
               createPortOnDevice(d,ser.getType()+" 0/"+i,ser);
           }
         }
        //switches
        if (d.getModel().getType().equals(Constants.MODEL_SWITCH_CiscoCatalyst2560V224TS)){
           for(int i=0; i<24;i++){
               createPortOnDevice(d,eth.getType()+" 0/"+i,eth);
           }
         }
        if (d.getModel().getType().equals(Constants.MODEL_SWITCH_CiscoCatalyst2560V248TS)){
           for(int i=0; i<48;i++){
               createPortOnDevice(d,eth.getType()+" 0/"+i,eth);
           }
         }
         //LRE Switches
        if (d.getModel().getType().equals(Constants.MODEL_LRESWITCH_CiscoCatalyst2912LREXL)){
           for(int i=0; i<12;i++){
               createPortOnDevice(d,lre.getType()+" 0/"+i,lre);
           }
           for(int i=0; i<4;i++){
               createPortOnDevice(d,eth.getType()+" 0/"+i,eth);
           }
         }
        if (d.getModel().getType().equals(Constants.MODEL_LRESWITCH_CiscoCatalyst2924LREXL)){
           for(int i=0; i<24;i++){
               createPortOnDevice(d,lre.getType()+" 0/"+i,lre);
           }
           for(int i=0; i<4;i++){
               createPortOnDevice(d,eth.getType()+" 0/"+i,eth);
           }
         }
        //CPE
        if (d.getModel().getType().equals(Constants.MODEL_CPE_Cisco575LRECPE)){
             createPortOnDevice(d,eth.getType()+" 0/0",eth);
             createPortOnDevice(d,rj11.getType()+" 0/0",rj11);
             createPortOnDevice(d,rj11.getType()+" 0/1",rj11);
         }

        //Splitter
        if (d.getModel().getType().equals(Constants.MODEL_SPLITTER_CiscoLRE48)){

         }
        //TODO splitter
        
    }

    /**
     * Creates a port on device
     * @param d device
     * @param name name of the port
     * @param portType type of the port
     */
    @Transactional
    private void createPortOnDevice(Device d,String name, PortType portType){
        Boolean isEnabled = false;
        Boolean isConnected = false;
        Port port = new Port(portType,name,isEnabled,isConnected);

        DevicePort devicePort = new DevicePort(d,port);

        devicePortDAO.persist(devicePort);
    }
}
